/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package net.worlds.lua;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.worlds.WorldsException;
import net.worlds.Main;
import net.worlds.object.GameObject;
import net.worlds.object.Script;
import se.krka.kahlua.vm.LuaClosure;
import se.krka.kahlua.vm.LuaState;


public class Event {
    private ArrayList<EventConnection> connections = new ArrayList<EventConnection>(4);
    public boolean disabled = false;
    public String eventName;
    private static HashMap<Class, HashMap<String, String>> eventArguments = new HashMap<Class, HashMap<String, String>>(64);
    private boolean firing = false;
    public static final Object[] NULL = new Object[]{};
    public static void addArguments(Class objectClass, String eventName, String arguments)
    {
        if(!eventArguments.containsKey(objectClass))
            eventArguments.put(objectClass, new HashMap<String, String>(16));
        if(!eventArguments.get(objectClass).containsKey(eventName))
            eventArguments.get(objectClass).put(eventName, arguments);
    }
    public static String getArguments(Class objectClass, String eventName)
    {
        return eventArguments.get(objectClass).get(eventName);
    }
    public void connect(Script s, ScriptThread t, LuaState l, LuaClosure c)
    {
        if(firing)
        {
            Main.log("Cannot use link() in this context");
            throw new RuntimeException("Cannot use link() in this context");
        }
        connections.add(new EventConnection(s, t, l, c));
    }
    public boolean hasConnections()
    {
        return !connections.isEmpty();
    }
    public void destroyConnectionsFor(LuaState l)
    {
        Iterator<EventConnection> itr = connections.iterator();
        while(itr.hasNext())
        {
            EventConnection c = itr.next();
            if(c.state == l)
                itr.remove();
        }
    }
    public void fire(final Object[] args)
    {
        if(disabled)
            return;
        //if(connections.size() > 0)
        //    new Throwable().printStackTrace();
        firing = true;
        for(final EventConnection c : connections)
        {
            if(System.currentTimeMillis() - c.lastFireTime < 50)
                continue;
            
            if(c.scriptThread != null)
            {
                c.scriptThread.addTask(new Runnable()
                {
                    public void run() {
                        LuaState newState = null;
                        try {
                            if ((eventName.equals("ChildRemoved")) || !(args.length > 0 && args[0] instanceof GameObject  && ((GameObject) args[0]).getParent() == null)) {
                                newState = Main.getScriptContext().newState();
                                newState.setEnvironment(c.state.getEnvironment());
                                newState.call(c.function, args);
                            }
                        } catch (Exception ex) {
                            c.scriptThread.handleExceptions(ex, newState);
                        }
                    }
                });
            }
            else
            {
                Runnable r = new Runnable()
                {
                    public void run() {
                        synchronized(c.state.getEnvironment())
                        {
                            try
                            {
                                c.state.call(c.function, args);
                            }
                            catch(RuntimeException ex)
                            {
                                String line = c.state.currentThread.stackTrace.split("\\n")[0];
                                System.err.println(line);
                                Main.error(ex);
                            }
                        }
                    }
                };
                Main.runInMainThread(r);
            }
            c.lastFireTime = System.currentTimeMillis();
        }
        firing = false;
    }
}
